// Flyweight factory.
// Copyright 2006 (C) Ralph Thomas

#include <view/gtk/flyweight/parent.h>
#include <view/gtk/flyweight/cell-renderer-icon.h>
#include <rb-cell-renderer-rating.h>
#include <rb-stock-icons.h>
#include <util/resources.h>
#include <factory.hpp>

#include <iostream>

namespace bfs = boost::filesystem;

namespace view {
	//
	// The flyweight parent does almost all of the hard work involving flyweights,
	// so we can build all of the different flyweights here without too much code.
	//
    void measure(GtkWidget* widget, GtkCellRenderer* target, adobe::eve_t::calculate_data_t& cdata) {
        int width, height;
        gtk_cell_renderer_get_size(target, widget, 0, 0, 0, &width, &height);
        cdata.horizontal().length_m = width;
        cdata.vertical().length_m = height;
    }
    void place(flyweight_parent* fwp, GtkCellRenderer* target, const adobe::eve_t::calculate_data_t&, const adobe::eve_t::place_data_t& pdata) {
        g_object_set(target, "xpad", 0, "ypad", 0, "width", pdata.horizontal().length_m, "height", pdata.vertical().length_m, NULL);
		fwp->position_renderer(target, pdata.horizontal().length_m, pdata.vertical().length_m, pdata.horizontal().position_m, pdata.vertical().position_m);
    }
    void insert_flyweight(flyweight_parent* fwp, GtkWidget* widget, GtkCellRenderer* target, const adobe::dictionary_t& params, adobe::eve_t& layout, const adobe::eve_t::iterator& parent) {
        adobe::eve_t::signal_suite_t signals;
        signals.eve_calculate_proc_m = boost::bind( &measure, widget, target, _1 );
        signals.eve_place_proc_m = boost::bind( &place, fwp, target, _1, _2 );
        layout.add_view_element( parent, adobe::eve_t::insert_element_t(
					adobe::eve_t::calculate_data_t(),
					false,
					params,
					signals )
				);
    }
	static void rated(GtkCellRenderer* r, const char* path, double rating, gpointer data) {
		flyweight_parent* fwp(reinterpret_cast<flyweight_parent*>(data));
		if (!fwp) return;
		fwp->set(adobe::value_t(rating));
	}
    adobe::widget_node_t flyweight_rating_factory(
            const adobe::dictionary_t&      parameters,
            const adobe::widget_node_t&     parent,
            const adobe::factory_token_t&   token)
    {
        //
        // We have to be a flyweight child.
        //
        flyweight_parent* real_parent = boost::any_cast<flyweight_parent*>( parent.display_token_m );
        if (!real_parent) throw std::runtime_error( "fw_rating is a flyweight widget, and may only be a child of a flyweight container!" );
        //
        // Make sure that we have the pixmaps this view requires loaded.
        //
        bfs::path icon_path = util::getResourcePath() / "images";
		rb_stock_icons_init( icon_path.native_file_string().c_str() );
		//
		// Now create the cell renderer.
		//
		GtkCellRenderer* r = rb_cell_renderer_rating_new();
		real_parent->adopt_renderer(r);

		g_signal_connect(r, "rated", G_CALLBACK(&rated), real_parent);
		//
		// Add the renderer to the layout.
		//
		insert_flyweight(real_parent, real_parent->get_widget(), r, parameters, token.layout_m, parent.eve_token_m);
		//
		// Give the parent our datapump.
		//
		real_parent->adopt_datapump(r, "rating", parameters.at(adobe::static_name_t("bind")));
		//
		// That's all.
		//
		return parent;
    }
	adobe::widget_node_t flyweight_icon_factory(
			const adobe::dictionary_t&		parameters,
			const adobe::widget_node_t&		parent,
			const adobe::factory_token_t&	token)
	{
		//
		// We have to be a flyweight child.
		//
		flyweight_parent* real_parent = boost::any_cast<flyweight_parent*>( parent.display_token_m );
		if (!real_parent) throw std::runtime_error( "fw_icon is a flyweight widget, and may only be a child of a flyweight container!" );
		//
		// Create the icon cell renderer.
		//
		GtkCellRenderer* r = mission_cell_renderer_icon_new();
		mission_cell_renderer_icon_set_notify_function(r, boost::bind( &flyweight_parent::redraw, real_parent, _1 ) );
		real_parent->adopt_renderer(r);
		real_parent->subscribe_index(r, "index");
		//
		// Add the renderer to the layout.
		//
		insert_flyweight(real_parent, real_parent->get_widget(), r, parameters, token.layout_m, parent.eve_token_m);
		//
		// Give the parent our icon name datapump.
		//
		real_parent->adopt_datapump(r, "icon-name", parameters.at(adobe::static_name_t("bind")));
		//
		// Cause the parent to invalidate whenever an icon gets
		// regenerated.
		//
		token.assemblage_m.hold_connection(mission_cell_renderer_icon_inval(boost::bind(&flyweight_parent::redraw_all, real_parent)));
		//
		// That's all.
		//
		return parent;
	}
}
